<?PHP
/**
 * OpenStation
 *
 * @Application : App.class.php
 * @Encode : 65001 Unicode ( UTF-8 NO BOM )
 * @Description : App 应用调度类
 * @Author : HK <hkyyang@qq.com>
 */

class App {

    /**
     * run 方法，应用运行入口
     *
     * @static
     * @access public
     * @param $
     * @return void
     */
    static public function run() {
        // 路由解析
        self :: routing();
        // 获取请求参数
        self :: get_request();
        // 初始化请求变量
        self :: var_init();
        // 执行应用
        self :: execute();
    }

    /**
     * execute 方法，执行一个应用
     *
     * @static
     * @access private
     * @param $
     * @return void
     */
    static private function execute() {
        // 当前应用的类名称
        Cache :: copy( 'SERVER.ACTION_SHORT', 'SERVER.CLASS', 'Action', '.=' );
        // 可能PATH_INFO会解析文件名为第一个参数，如果这里是就出栈
        if( Cache :: get( 'SERVER.REQUEST.0' ) == basename( APP_FILE ) || ( Cache :: get( '@.MAIN.URL_MODE' ) == URL_REWRITE && Cache :: get( 'SERVER.REQUEST.0' ) == get_file_name( basename( APP_FILE ) ) ) ) {
            Cache :: shift( 'SERVER.REQUEST' );
        }
        if( count( Cache :: get( 'SERVER.REQUEST' ) ) ) {
            // 设置本次执行的行为方法，第一个请求参数为方法名，所以这里出栈
            Cache :: set( 'SERVER.METHOD', Cache :: shift( 'SERVER.REQUEST' ) );
        } else {
            // 当没有请求参数时，使用系统默认方法
            Cache :: set( 'SERVER.METHOD', 'index' );
        }

        Cache :: set( 'SERVER.METHOD', str_replace( '.', '#', Cache :: get( 'SERVER.METHOD' ) ) );

        // 载入项目当前行为类
        import( Cache :: get( 'SERVER.ACTION_FILE_NAME' ) );
        // 获取项目当前行为实例
        $action = Cache :: get_instance( 'SERVER.CLASS' );
        // 行为方法不存在
        if( ! method_exists( $action, Cache :: get( 'SERVER.METHOD' ) ) ) {
            // 尝试行为方法别名
            self :: parseActionAlias();
        } else {
            $action -> CurrentMethod = Cache :: get( 'SERVER.METHOD' );
            // 禁止直接访问别名方法
            if( ! Cache :: is_empty( '@.ACTION_ALIAS.' . Cache :: get( 'SERVER.ACTION_SHORT' ) ) ) {
                foreach( Cache :: get( '@.ACTION_ALIAS.' . Cache :: get( 'SERVER.ACTION_SHORT' ) ) as $method ) {
                    if( ( is_string( $method ) && $method == $action -> CurrentMethod ) || ( is_array( $method ) && key( $method ) == $action -> CurrentMethod ) ) {
                        notfound( FALSE );
                        break;
                    }
                }
            }
        }

        // 优先别名目标方法
        if( method_exists( $action, Cache :: get( 'SERVER.TARGET_METHOD' ) ) ) {
            $action -> CurrentMethod = Cache :: get( 'SERVER.TARGET_METHOD' );
        // 当前执行的方法不存在
        } else if( ! method_exists( $action, Cache :: get( 'SERVER.METHOD' ) ) ) {
            // 开启了调试模式,显示一个异常
            if( Cache :: get( '@.ERROR.DEBUG' ) ) {
                Cache :: set( 'OPENSTATION.ERROR.EASY_DEBUG', ON );
                throw_exception( Cache :: lang( 'ACTION_NOT_EXIST' ) . ': ' . Cache :: get( 'SERVER.METHOD' ) );
            // 根据配置显示错误页面
            } else {
                notfound( FALSE );
            }
        }

        // 禁止直接执行构造和析构
        if( in_array( $action -> CurrentMethod, array( '__initial', '__finish' ) ) ) {
            notfound( FALSE );
        }

        // 禁止访问非公开行为方法
        if( method_exists( 'ReflectionMethod', FALSE ) ) {
            $Reflection = new ReflectionMethod( $action, $action -> CurrentMethod );
            if( FALSE == $Reflection -> isPublic() ) {
                notfound( FALSE );
            }
        }

        // 执行当前行为的构造方法
        $action -> __initial();
        // 执行当前行为方法
        if( ! is_null( Cache :: get( 'SERVER.TARGET_METHOD' ) ) ) {
            // 有参数的别名方法
            if( ! is_null( Cache :: get( 'SERVER.METHOD_ATTR' ) ) ) {
                Cache :: call( 'SERVER.TARGET_METHOD', Cache :: get( 'SERVER.METHOD_ATTR' ), $action );
            } else {
                Cache :: call( 'SERVER.TARGET_METHOD', NULL, $action );
            }
        } else {
            Cache :: call( 'SERVER.METHOD', NULL, $action );
        }
        // 执行当前行为的析构方法
        $action -> __finish();

        // 开启了调试，显示已经存在的错误和警告
        if( Cache :: type( 'SERVER.ERROR', 'array' ) && Cache :: get( '@.ERROR.DEBUG' ) ) {
            include Cache :: get( 'OPENSTATION.ERROR.ERROR' );
        }

    }

    /**
     * var_init 方法，变量初始化设置
     *
     * @static
     * @access private
     * @param $
     * @return void
     */
    static private function var_init() {
        if( Cache :: get( 'SERVER.PATH_INFO' ) ) {
            $vars = Cache :: get( 'SERVER.PATH_INFO' );
            $vars = explode( Cache :: get( 'OPENSTATION.URL.PATHINFO_DEPR' ), $vars );
            Cache :: set( 'SERVER.REQUEST', $vars );
        }
        // 自动开启SESSION会话
        if( Cache :: get( '@.SESSION.AUTO_START' ) ) {
            import( 'Lib.Net.Session' );
            Session :: start();
        }

        // HTTP变量设置
        Cache :: copy( 'SERVER.ROOT', 'HTTP.ROOT' );
        Cache :: copy( 'SERVER.URL', 'HTTP.URL' );
        Cache :: copy( 'SERVER.SELF', 'HTTP.SELF' );
        if( Cache :: get( '@.HTTP.STATIC' ) ) {
            Cache :: copy( '@.HTTP.STATIC', 'HTTP.STATIC' );
        } else {
            Cache :: copy( 'HTTP.ROOT', 'HTTP.STATIC', 'Static', '.=' );
        }
        Cache :: set( 'HTTP.STATIC', string_after_remove( Cache :: get( 'HTTP.STATIC' ), '/' ) );
        if( isset( $_SERVER[ 'HTTP_REFERER' ] ) ) {
            Cache :: set( 'HTTP.REFERER', $_SERVER[ 'HTTP_REFERER' ] );
        }

        if( ! empty( $_SERVER[ 'HTTP_X_REQUESTED_WITH' ] ) && strtolower( $_SERVER[ 'HTTP_X_REQUESTED_WITH' ] ) == 'xmlhttprequest' ) {
            Cache :: set( 'HTTP.AJAX', 1 );
        }

    }

    /**
     * routing 方法，路由解析
     *
     * @static
     * @access private
     * @param $
     * @return void
     */
    static private function routing() {

        if( isset( $_SERVER[ 'HTTPS' ] ) && strtolower( $_SERVER[ 'HTTPS' ] ) == 'on' ) {
            Cache :: set( 'HTTP.SSL', TRUE );
        }
        Cache :: set( 'SERVER.SELF', Cache :: get( 'HTTP.SSL' ) ? 'https://' : 'http://' . $_SERVER[ 'HTTP_HOST' ] . $_SERVER[ 'REQUEST_URI' ] );
        $url_info = parse_url( Cache :: get( 'SERVER.SELF' ) );
        if( strpos( $url_info[ 'path' ], Cache :: get( 'SERVER.ENTRANCE' ) ) !== FALSE ) {
            $server_root = $url_info[ 'scheme' ] . '://' . $url_info[ 'host' ] . substr( $url_info[ 'path' ], 0, strpos( $url_info[ 'path' ], Cache :: get( 'SERVER.ENTRANCE' ) ) );
        } else {
            $server_root = $url_info[ 'scheme' ] . '://' . $url_info[ 'host' ] . str_replace( $_SERVER[ 'DOCUMENT_ROOT' ], '', dirname( $_SERVER[ 'SCRIPT_FILENAME' ] ) . '/' );
        }
        $server_root = string_append_replace( $server_root, '/' );

        if( Cache :: get( '@.MAIN.URL_MODE' ) == URL_REWRITE && strpos( $_SERVER[ 'REQUEST_URI' ], Cache :: get( 'SERVER.ENTRANCE' ) ) !== FALSE ) {
            $server_url = str_replace( '/' . Cache :: get( 'SERVER.ENTRANCE' ), '', $server_root );
        } else {
            $server_url = $server_root . Cache :: get( 'SERVER.ENTRANCE' );
        }
        $server_url = string_after_remove( $server_url, '/' );

        Cache :: set( 'SERVER.URL', $server_url );
        Cache :: set( 'SERVER.ROOT', $server_root );

        $pathinfo = str_replace( Cache :: get( 'SERVER.ROOT' ), '', Cache :: get( 'SERVER.SELF' ) );
        $pathinfo = string_before_remove( $pathinfo, Cache :: get( 'SERVER.ENTRANCE' ) );
        $pathinfo = string_before_remove( $pathinfo, 'http://' . $_SERVER[ 'HTTP_HOST' ] . '/' );

        if( strlen( $pathinfo ) > 0 && in_array( $pathinfo{0}, array( '?', Cache :: get( 'OPENSTATION.URL.PATHINFO_DEPR' ) ) ) ) {
            $pathinfo = substr( $pathinfo, 1 );
        }

        if( strpos( $pathinfo, '?' ) !== FALSE ) {
            $pathinfo = substr( $pathinfo, strpos( $pathinfo, '?' ) + 1 );
        }

        // NORMAL || AUTO 模式，优先判断GET传递参数
        if( count( $_GET ) ) {
            if( isset( $_GET[ Cache :: get( 'OPENSTATION.URL.PATHINFO_AUTONAME' ) ] ) ) {
                $pathinfo = trim( $_GET[ Cache :: get( 'OPENSTATION.URL.PATHINFO_AUTONAME' ) ] );
            } else if( strlen( $pathinfo ) > 0 ) {
                $pathinfo = str_replace( '&', Cache :: get( 'OPENSTATION.URL.PATHINFO_DEPR' ), $pathinfo );
                $pathinfo = explode( Cache :: get( 'OPENSTATION.URL.PATHINFO_DEPR' ), $pathinfo );
                array_walk( $pathinfo, array( 'self', 'parse_get_var' ) );
                $pathinfo = implode( Cache :: get( 'OPENSTATION.URL.PATHINFO_DEPR' ), $pathinfo );
            }
        }
        // POST 变量收集
        if( ! empty( $_POST ) ) {
            Cache :: set( 'HTTP.POST', $_POST, NULL, FALSE );
            unset( $_POST );
        }

        Cache :: set( 'SERVER.PATH_INFO', string_after_remove( $pathinfo, '/' ) );

    }

    /**
     * parseActionAlias 方法，行为别名解析
     *
     * @static
     * @access private
     * @param $
     * @return void
     */
    static private function parseActionAlias() {

        function alias_param_replace( $attr ) {
            return '(?<' . $attr . '>.+?)';
        }

        if( ! Cache :: is_empty( '@.ACTION_ALIAS.' . Cache :: get( 'SERVER.ACTION_SHORT' ) ) ) :
            if( ! is_null( Cache :: get( '@.ACTION_ALIAS.' . Cache :: get( 'SERVER.ACTION_SHORT' ) . '.' . Cache :: get( 'SERVER.METHOD' ) ) ) ) {
                // Array
                if( Cache :: type( '@.ACTION_ALIAS.' . Cache :: get( 'SERVER.ACTION_SHORT' ) . '.' . Cache :: get( 'SERVER.METHOD' ), 'array' ) ) {
                    $target_method = Cache :: get( '@.ACTION_ALIAS.' . Cache :: get( 'SERVER.ACTION_SHORT' ) . '.' . Cache :: get( 'SERVER.METHOD' ) );
                    Cache :: set( 'SERVER.TARGET_METHOD', key( $target_method ) );
                    Cache :: set( 'SERVER.METHOD_ATTR', Cache :: get( '@.ACTION_ALIAS.' . Cache :: get( 'SERVER.ACTION_SHORT' ) . '.' . Cache :: get( 'SERVER.METHOD' ) . '.' . Cache :: get( 'SERVER.TARGET_METHOD' ) ) );
                // String
                } else if( Cache :: type( '@.ACTION_ALIAS.' . Cache :: get( 'SERVER.ACTION_SHORT' ) . '.' . Cache :: get( 'SERVER.METHOD' ), 'string' ) ) {
                    Cache :: copy( '@.ACTION_ALIAS.' . Cache :: get( 'SERVER.ACTION_SHORT' ) . '.' . Cache :: get( 'SERVER.METHOD' ), 'SERVER.TARGET_METHOD' );
                }
            } else {
                foreach( Cache :: get( '@.ACTION_ALIAS.' . Cache :: get( 'SERVER.ACTION_SHORT' ) ) as $mth => $param ) :
                    if( ! preg_match( '/\[\$.+?\]/', $mth ) ) {
                        continue;
                    }
                    $reg = '/' . preg_replace( '/\[\$(.+?)\]/ie', "alias_param_replace( '\$1' )", str_replace( '.', '\.', $mth ) ) . '/i';
                    $tmp = array();
                    if( preg_match( $reg, str_replace( '#', '.', Cache :: get( 'SERVER.METHOD' ) ), $tmp ) ) {
                        Cache :: set( 'SERVER.TARGET_METHOD', key( $param ) );
                        $params = array_shift( $param );
                        foreach( $params as $p ) {
                            Cache :: append( 'SERVER.METHOD_ATTR', strtoupper( $tmp[ $p ] ) );
                        }
                        break;
                    }
                endforeach;
            }
        endif;
    }

    /**
     * 回调方法，参数解析并缓存
     *
     * @static
     * @callback
     * @access private
     * @param * : & $value
     * @return void
     */
    static private function parse_get_var( & $value ) {

        if( strpos( $value, '=' ) !== FALSE ) {
            $key = substr( $value, 0, strpos( $value, '=' ) );
            $value = substr( $value, strpos( $value, '=' ) + 1 );
        }
        if( ! empty( $key ) ) {
            Cache :: set( 'HTTP.GET.' . $key, $value, NULL, FALSE );
        }
    }

    /**
     * 获取本次行为的请求参数
     *
     * @static
     * @access private
     * @param $
     * @return void
     */
    static private function get_request() {
        $vars = array();
        if( Cache :: get( 'SERVER.PATH_INFO' ) ) {
            $vars = Cache :: get( 'SERVER.PATH_INFO' );
            $vars = explode( Cache :: get( 'OPENSTATION.URL.PATHINFO_DEPR' ), $vars );
        }
        Cache :: set( 'SERVER.REQUEST', $vars );
    }

}

?>